package sh.orm.convert;

import sh.base.aop.util.ReflectUtil;
import sh.base.aop.util.ScannerUtil;
import sh.base.config.ConfigManager;
import sh.orm.ConfigManagerInitiator;
import sh.orm.OrmConst;
import sh.orm.OrmException;
import sh.orm.util.ClassUtil;

import java.math.BigDecimal;
import java.sql.Ref;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 类型转化的注册中心
 * 注册默认的类型转换器，并注册用户自定义的类型转换器
 * @author lqq
 * @date 2019/2/27
 */
public class TypeConvertorRegistry {

    private static TypeConvertorRegistry instance;

    /**
     *  类型转换器的map, key是java的classname+jdbc数据类型
     */
    private Map<String, TypeConvertor> convertorMap = new HashMap<>();

    /**
     * 注册已知的类型转换器
     *
     */
    private TypeConvertorRegistry(){

        // TODO 需要注册基本的数据类型转换器
        //    原始数据类型
        register(String.class, JdbcType.VARCHAR, new StringTypeConverter());
        register(Character.class, JdbcType.CHAR, new StringTypeConverter());
        register(char.class, JdbcType.CHAR, new StringTypeConverter());

        register(Long.class, JdbcType.BIGINT, new LongTypeConverter());
        register(long.class, JdbcType.BIGINT, new LongTypeConverter());
        register(Integer.class, JdbcType.INTEGER, new IntegerTypeConverter());
        register(int.class, JdbcType.INTEGER, new IntegerTypeConverter());
        register(Float.class, JdbcType.FLOAT, new FloatTypeConverter());
        register(float.class, JdbcType.FLOAT, new FloatTypeConverter());
        register(Double.class, JdbcType.DOUBLE, new DoubleTypeConverter());
        register(double.class, JdbcType.DOUBLE, new DoubleTypeConverter());

        register(BigDecimal.class, JdbcType.DECIMAL, new BigDecimalTypeConverter());
        register(Date.class, JdbcType.DATE, new DateTypeConvertor());
        register(Date.class, JdbcType.TIMESTAMP, new DateTypeConvertor());

        registerCustomConvertor();

    }


    /**
     * 注册用户自定义类型转换器
     */
    private void registerCustomConvertor(){

        ConfigManager configManager = ConfigManagerInitiator.getInstance().getConfigManager();
        String converterScanPath = configManager.getProperties().getProperty(OrmConst.TYPE_CONVERTOR_SCAN_PATH);
        if(converterScanPath == null){
            //如果没有配，说明不需要类型转换
            return;
        }

        // 扫描
        Map<Class<?>,MappedTypes> convertorMappedTypesMap
                = ScannerUtil.getClassesWithTpyeAnnotation(converterScanPath, true, MappedTypes.class);
        for(Map.Entry<Class<?>, MappedTypes> entry : convertorMappedTypesMap.entrySet()){
            register(entry.getValue().returnType(),
                    entry.getValue().jdbcType(),
                    (TypeConvertor) ReflectUtil.getInstance(entry.getKey()));
        }
    }


    public static TypeConvertorRegistry getInstance() {
        if(instance != null){
            return instance;
        }else{
            synchronized (TypeConvertorRegistry.class){
                if(instance == null){
                    instance = new TypeConvertorRegistry();
                }
            }
        }
        return instance;
    }

    /**
     * 注册类型转换器,
     * 如果用户有自定义的类型转换器，需要通过这个方法注册进来
     * @param javaClass
     * @param jdbcType
     * @param typeConvertor
     */
    public void register(Class<?> javaClass, JdbcType jdbcType, TypeConvertor typeConvertor){
        convertorMap.put(getKey(javaClass, jdbcType), typeConvertor);
    }


    /**
     * 根据java对象和数据库的类型就可以得到对应的类型转换器
     * @param javaClass
     * @param jdbcType
     * @return
     */
    public TypeConvertor getTypeConvertor(Class<?> javaClass, JdbcType jdbcType){
        Class<?> realClass = ClassUtil.getRealEntityClass(javaClass);
        TypeConvertor typeConvertor = this.convertorMap.get(getKey(realClass, jdbcType));
        if(typeConvertor == null){
            throw new OrmException("没有对应的类型转换器 javaClass:" + realClass + " jdbcType:" + jdbcType);
        }
        return  typeConvertor;
    }

    /**
     * 获取key，可以优化获取key的方式
     * @param javaClass
     * @param jdbcType
     * @return
     */
    private String getKey(Class<?> javaClass, JdbcType jdbcType){
        return javaClass.getName() + jdbcType.name();
    }
}
